package com.net.xpay.core.service.impl;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.domain.UserProfile;
import com.net.xpay.common.domain.UserShard;
import com.net.xpay.common.manager.UserManager;
import com.net.xpay.common.manager.UserProfileManager;
import com.net.xpay.common.manager.UserShardManager;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.validator.CollectionOrderValidator;
import com.net.common.annotation.WriteTransactional;
import com.net.common.exception.BusinessException;
import com.net.common.util.StreamUtil;
import com.net.xpay.alipay.enums.IdentityType;
import com.net.xpay.core.domain.PayTrans;
import com.net.xpay.core.domain.PayTransBatch;
import com.net.xpay.core.domain.collect.CollectionBatch;
import com.net.xpay.core.domain.collect.CollectionOrder;
import com.net.xpay.core.enums.CollectionOrderStatus;
import com.net.xpay.core.enums.PayTransBatchStatus;
import com.net.xpay.core.enums.PayTransStatus;
import com.net.xpay.core.manager.collect.CollectionBatchManager;
import com.net.xpay.core.manager.collect.CollectionOrderManager;
import com.net.xpay.core.model.CollectionOrderModel;
import com.net.xpay.core.model.CollectionOrderTransModel;
import com.net.xpay.core.model.InnerPayTransBatchModel;
import com.net.xpay.core.model.PayTransModel;
import com.net.xpay.core.service.CollectionOrderService;
import com.net.xpay.core.service.InnerPayTransService;
import com.net.shard.ShardUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class CollectionOrderServiceImpl implements CollectionOrderService {

    @Autowired
    private CollectionOrderManager collectionOrderManager;

    @Autowired
    private CollectionOrderValidator collectionOrderValidator;

    @Autowired
    private UserManager userManager;

    @Autowired
    private UserProfileManager userProfileManager;

    @Autowired
    private UserShardManager userShardManager;

    @Autowired
    private InnerPayTransService innerPayTransService;

    @Autowired
    private CollectionBatchManager collectionBatchManager;

    @Override
    public CollectionOrder addCollectionOrder(CollectionOrderModel model) {
        collectionOrderValidator.validateAddCollectionOrder(model);

        User user = userManager.findByUid(model.getUid());
        Preconditions.checkArgument(user.getUid().equals(model.getUid()));
        UserProfile userProfile = userProfileManager.getById(user.getId());
        if (!Boolean.TRUE.equals(userProfile.getOpenCollectionSafePay())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "收集功能已被关闭");
        }

        UserShard userShard = userShardManager.getById(user.getId());
        try {
            ShardUtil.setShardContext(userShard.getShardId());
            CollectionOrder order = CollectionOrder.getInstance();
            order.setName(model.getName());
            order.setIdentity(model.getIdentity());
            order.setIdentityType(IdentityType.ALIPAY_LOGON_ID.getValue());
            order.setTransAmount(new BigDecimal(model.getTransAmount()));
            order.setUserId(user.getId());
            order.setStatus(CollectionOrderStatus.INIT);
            collectionOrderManager.save(order);

            return order;
        } finally {
            ShardUtil.cleanShardContext();
        }
    }

    @Override
    @WriteTransactional
    public String trans(User merchant, CollectionOrderTransModel transModel) {
        if (CollectionUtils.isEmpty(transModel.getOrderIds())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请至少选择一条收款订单");
        }
        if (Strings.isNullOrEmpty(merchant.getAlipayNickName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请先签约一个转账支付宝账号");
        }
        if (transModel.getPayMethod() == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请选择支付方式");
        }

        List<CollectionOrder> orders = new ArrayList<>();
        BigDecimal totalAmount = new BigDecimal(0);
        for (Long orderId : transModel.getOrderIds()) {
            CollectionOrder order = collectionOrderManager.getById(orderId);
            if (order == null) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "收款订单不存在");
            }
            if (order.getStatus() != CollectionOrderStatus.INIT) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "收款订单状态不为待转账状态 无法转账");
            }
            totalAmount = totalAmount.add(order.getTransAmount());
            orders.add(order);
            order.setStatus(CollectionOrderStatus.DEALING);
            collectionOrderManager.updateByPrimaryKeyWithVersion(order);
        }
        //生成批次记录
        CollectionBatch batch = CollectionBatch.getInstance();
        batch.setUserId(merchant.getId());
        batch.setStatus(PayTransBatchStatus.WAIT_PAY);
        batch.setTotalTransAmount(totalAmount.toString());
        batch.setTotalCount((long) orders.size());
        batch.setDealingTime(LocalDateTime.now());
        batch.setOrderIds(transModel.getOrderIds());
        collectionBatchManager.save(batch);

        //发起转账
        InnerPayTransBatchModel payTransBatchModel = new InnerPayTransBatchModel();
        payTransBatchModel.setAlipayNickName(merchant.getAlipayNickName());
        payTransBatchModel.setMobile(merchant.getMobile());
        payTransBatchModel.setImportBatchNo(batch.getBizId());
        List<PayTransModel> payTrans = new ArrayList<>();

        for (CollectionOrder order : orders) {
            PayTransModel payTransModel = new PayTransModel();
            payTransModel.setImportBizNo(order.getId().toString());
            payTransModel.setTransAmount(order.getTransAmount().toString());
            payTransModel.setIdentity(order.getIdentity());
            payTransModel.setName(order.getName());
            payTrans.add(payTransModel);
        }

        payTransBatchModel.setPayTrans(payTrans);
        payTransBatchModel.setPayMethod(transModel.getPayMethod());

        String payUrl = innerPayTransService.batchPayTrans(payTransBatchModel);
        batch.setLastPayUrl(payUrl);
        collectionBatchManager.updateByPrimaryKeyWithVersion(batch);
        return payUrl;
    }

    @Override
    @WriteTransactional
    public void updateCollectionBatch(User user, PayTransBatch batch, List<PayTrans> payTransList) {
        if (batch.getEndTime() == null) {
            return;
        }
        CollectionBatch collectionBatch = collectionBatchManager.findByBizId(user.getId(), batch.getImportBatchNo());
        if (collectionBatch == null) {
            log.error("updateCollectionBatch error batch is not find userId={}|batchId={}", user.getId(), batch.getId());
            return;
        }

        collectionBatch.setSuccessCount(batch.getSuccessCount());
        collectionBatch.setSuccessAmount(batch.getSuccessAmount());
        collectionBatch.setFailCount(batch.getFailCount());
        collectionBatch.setFailedAmount(batch.getFailedAmount());
        collectionBatch.setEndTime(batch.getEndTime());
        collectionBatch.setStatus(batch.getStatus());
        collectionBatch.setBatchTransId(batch.getBatchTransId());
        collectionBatchManager.updateByPrimaryKeyWithVersion(collectionBatch);

        List<CollectionOrder> orders = collectionOrderManager.findByIds(collectionBatch.getOrderIds());

        Map<String/*importBizNo*/, PayTrans> payTransMap = StreamUtil.toMap(payTransList, PayTrans::getImportBizNo);
        for (CollectionOrder order : orders) {
            PayTrans payTrans = payTransMap.get(order.getId().toString());
            if (payTrans.getStatus() == PayTransStatus.SUCCESS) {
                order.setStatus(CollectionOrderStatus.SUCCESS);
            } else {
                order.setStatus(CollectionOrderStatus.FAIL);
                order.setErrorCode(payTrans.getErrorCode());
                order.setErrorMsg(payTrans.getErrorMsg());
            }

            order.setEndTime(payTrans.getEndTime());
        }
        collectionOrderManager.batchUpdate(orders);
    }
}
